package changgou.search.service.impl;

import changgou.search.dao.SkuEsMapper;
import changgou.search.service.SkuService;
import com.alibaba.fastjson.JSON;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.pojo.SkuInfo;
import entity.Result;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.sql.SQLException;
import java.util.*;

@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private SkuEsMapper skuEsMapper;

    /*
    * ElasticsearchTemplate : 可以实现索引库的增删改查[高级搜索]
    * */
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    /*
     * 导入索引库
     * */
    @Override
    public void importData() {
        Result<List<Sku>> skuResult = skuFeign.findAll();

        List<SkuInfo> skuInfoList = JSON.parseArray(JSON.toJSONString(skuResult.getData()), SkuInfo.class);

        for (SkuInfo skuInfo : skuInfoList) {
            Map<String,Object> specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
            //当前Map<String,Object>后面的Object的值会作为当前SKu对象该域（key）对应的值
            skuInfo.setSpecMap(specMap);
        }

        skuEsMapper.saveAll(skuInfoList);
    }




    /*
    * 多条件搜索
    * */

    @Override
    public Map<String, Object> search(Map<String, String> searchMap) throws SQLException {

        //搜索条件封装
        NativeSearchQueryBuilder nativeSearchQueryBuilder = builBaseicQuery(searchMap);

        //集合搜索
        Map<String,Object> resultMap = searchList(nativeSearchQueryBuilder);


        //分组查询分类集合
        //当用户选择了分类，将分类作为搜索条件，则不需要对分类进项分组搜索，因为分组搜索的数据是用于显示分类搜索条件的
        //分类->searchMap->category
        //if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))){
           // List<String> categoryList = searchCategoryList(nativeSearchQueryBuilder);
           // resultMap.put("categoryList",categoryList);
       // }

        //查询品牌集合
        //当用户选择了品牌，将品牌作为搜索条件，则不需要对品牌进项分组搜索，因为分组搜索的数据是用于显示品牌搜索条件的
        //分类->searchMap->category
        //if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))){
           // List<String> brandList = searchBrandList(nativeSearchQueryBuilder);
            //resultMap.put("brandList",brandList);
        //}


        //规格查询
        //<String, Set<String>> specList = searchSpecList(nativeSearchQueryBuilder);
        //resultMap.put("specList",specList);
        Map<String, Object> groupMap = searchGroupList(nativeSearchQueryBuilder, searchMap);
        resultMap.putAll(groupMap);
        return resultMap;
    }

    //搜索条件封装
    private NativeSearchQueryBuilder builBaseicQuery(Map<String, String> searchMap) {
        //搜索条件构造对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //BoolQuery
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if(searchMap!=null && searchMap.size()>0){
            //根据关键词搜索
            String keywords = searchMap.get("keywords");
            //如果关键词不为空，则搜索关键词数据
            if(!StringUtils.isEmpty(keywords)){
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keywords).field("name"));
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(keywords).field("name"));
            }

            //输入了分类->category
            if(!StringUtils.isEmpty(searchMap.get("category"))){
                boolQueryBuilder.must(QueryBuilders.termQuery("categoryName",searchMap.get("category")));
            }

            //输入了品牌->brand
            if(!StringUtils.isEmpty(searchMap.get("brand"))){
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName",searchMap.get("brand")));
            }

            //规格过滤实现：spec_网络=联通3G&spec_颜色=红
            for (Map.Entry<String, String> entry : searchMap.entrySet()) {
                String key = entry.getKey();
                //如果key以sepc_开始,则表示规格筛选查询
                if(key.startsWith("spec_")){
                    //规格条件的值
                    String value = entry.getValue();
                    boolQueryBuilder.must(QueryBuilders.termQuery("specMap."+key.substring(5)+".keyword",value));
                }
            }
        }

        String price = searchMap.get("price");
        if(!StringUtils.isEmpty(price)){
            price.replace("元","").replace("以上","");
            String[] prices = price.split("-");
            if(prices!=null && prices.length >0){
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0])));
                if(prices.length==2){
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1])));
                }
            }

            //排序实现
            String sortField = searchMap.get("sortField");
            String sortRule = searchMap.get("sortRule");
            if(!StringUtils.isEmpty(sortField)&& !StringUtils.isEmpty(sortRule)){
                nativeSearchQueryBuilder.withSort(
                        new FieldSortBuilder(sortField).
                                order(SortOrder.valueOf(sortRule))
                );
            }
        }

        //分页，用户如果不传分页参数，则默认第一页
        Integer pageNum = coverterPage(searchMap);
        Integer size = 5;
        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1,size));

        //将boolQueryBuilder填充给nativeSearchQueryBuilder
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        return nativeSearchQueryBuilder;
    }



    /*
     * 分组查询->分类、品牌、规格查询
     * */
    public Map<String,Object> searchGroupList(NativeSearchQueryBuilder nativeSearchQueryBuilder,Map<String,String> searchMap){

        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        }
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("Brand"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        }
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);

        //定义一个Map,存储所有分组结果
        Map<String,Object> groupMapResult = new HashMap<String,Object>();

        //获取分组数据
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            StringTerms categoryTerms = aggregatedPage.getAggregations().get("skuCategory");
            List<String> categoryList = getGroupList(categoryTerms);
            groupMapResult.put("categoryList",categoryList);
        }
        if(searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            StringTerms brandTerms = aggregatedPage.getAggregations().get("skuBrand");
            List<String> brandList = getGroupList(brandTerms);
            groupMapResult.put("brandList",brandList);
        }
        StringTerms specTerms = aggregatedPage.getAggregations().get("skuSpec");
        List<String> specList = getGroupList(specTerms);

        Map<String, Set<String>> specMap = putAllSpec(specList);
        groupMapResult.put("specList",specMap);

        return groupMapResult;
    }


    public List<String> getGroupList(StringTerms stringTerms){
        List<String> groupList = new ArrayList<String>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String feildName = bucket.getKeyAsString();
            groupList.add(feildName);
        }
        return groupList;
    }


    /*
    * 接收前端传入的分页参数
    *
    * */
    public Integer coverterPage(Map<String,String> searchMap){
        if(searchMap!=null){
            String pageNum = searchMap.get("pageNum");
            try{
                return Integer.parseInt(pageNum);
            }catch (NumberFormatException e){

            }
        }
        return 1;
    }

    //返回结果
    private Map<String, Object> searchList(NativeSearchQueryBuilder nativeSearchQueryBuilder) {

        //高亮配置
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");
        //前缀
        field.preTags("<em style=\"color:red;\">");
        //后缀
        field.postTags("</em>");
        //碎片长度 关键词数据的长度
        field.fragmentSize(100);
        //添加高亮
        nativeSearchQueryBuilder.withHighlightFields(field);



        //执行搜索
        //AggregatedPage<SkuInfo> page = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);
        AggregatedPage<SkuInfo> page = elasticsearchTemplate.
                queryForPage(
                        nativeSearchQueryBuilder.build(),//搜索条件封装
                        SkuInfo.class,                  //数据集合要转换的类型的字节码
                        //SearchResultMapper);             //执行搜索后，将数据结果集封装到对象中
                        new SearchResultMapper() {
                            @Override
                            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                                //存储所有转换后的高亮数据对象
                                List<T> list = new ArrayList<T>();
                                //执行查询，获取所有数据->结果集[非高亮数据|高亮数据]
                                for (SearchHit hit : response.getHits()) {
                                    //分析结果集数据，获取非高亮数据
                                    SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);

                                    //分析结果数据，获取高亮数据->只有某个域的高亮数据
                                    HighlightField highlightField = hit.getHighlightFields().get("name");

                                    if(highlightField!=null && highlightField.getFragments()!=null){
                                        //高亮数据读取出来
                                        Text[] fragments = highlightField.getFragments();
                                        StringBuffer buffer = new StringBuffer();
                                        for (Text fragment : fragments) {
                                            buffer.append(fragment.toString());
                                        }
                                        //非高亮数据中指定的域替换成高亮数据
                                        skuInfo.setName(buffer.toString());
                                    }
                                    //将高亮数据添加到集合中
                                    list.add((T) skuInfo);
                                }
                                return new AggregatedPageImpl<T>(list,pageable,response.getHits().getTotalHits());
                            }
                        });


        //分页参数-总记录数
        long totalElements = page.getTotalElements();
        //总页数
        int totalPages = page.getTotalPages();
        //获取数据结果集
        List<SkuInfo> contents = page.getContent();
        //封装一个Map存储所有数据，并返回
        Map<String,Object> resultMap = new HashMap<String,Object>();
        resultMap.put("rows",contents);
        resultMap.put("total",totalElements);
        resultMap.put("totalPages",totalPages);

        return resultMap;
    }



    /*
    * 分类分组查询
    * */
    public List<String> searchCategoryList(NativeSearchQueryBuilder nativeSearchQueryBuilder){
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);


        //获取分组数据
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuCategory");
        List<String> categoryList = new ArrayList<String>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String categoryName = bucket.getKeyAsString();
            categoryList.add(categoryName);
        }
        return categoryList;
    }

    /*
     * 品牌分组查询
     * */
    public List<String> searchBrandList(NativeSearchQueryBuilder nativeSearchQueryBuilder){
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);


        //获取分组数据
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuBrand");
        List<String> brandList = new ArrayList<String>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String brandName = bucket.getKeyAsString();
            brandList.add(brandName);
        }
        return brandList;
    }

    /*
     * 规格分组查询
     * */
    public Map<String, Set<String>> searchSpecList(NativeSearchQueryBuilder nativeSearchQueryBuilder){
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword").size(10000));
        AggregatedPage<SkuInfo> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);


        //获取分组数据
        StringTerms stringTerms = aggregatedPage.getAggregations().get("skuSpec");
        List<String> specList = new ArrayList<String>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String specName = bucket.getKeyAsString();
            specList.add(specName);
        }

        //规格汇总合并
        Map<String,Set<String>> allSpec = putAllSpec(specList);

        return allSpec;
    }


    //规格汇总合并
    private Map<String, Set<String>> putAllSpec(List<String> specList) {
        //合并后的Map对象,将每个Map对象合成一个Map<String,set<String>>
        HashMap<String, Set<String>> allSpec = new HashMap<String, Set<String>>();

        //1.循环specList
        for (String spec : specList) {
            //2.将每个JSON字符串转成Map
            Map<String,String> specMap = JSON.parseObject(spec, Map.class);


            //4.合并流程,循环所有的Map
            for (Map.Entry<String, String> entry : specMap.entrySet()) {
                //4.1取出当前Map
                String key = entry.getKey();
                String value = entry.getValue();
                //4.2将当前虚拟换的数据合并到一个Map<String,set<String>>中
                //从allSpec中获取当前规格对应的Set集合数据
                Set<String> specSet = allSpec.get(key);
                if(specSet == null){
                    //之前allSpec中没有该规格
                    specSet = new HashSet<String>();
                }
                specSet.add(value);
                allSpec.put(key,specSet);

            }
        }
        return allSpec;
    }
}
